//*******************************************************************       //
//IMPORTANT NOTICE                                                          //
//================                                                          //
//Copyright Mentor Graphics Corporation 1996 - 1999.  All rights reserved.  //
//This file and associated deliverables are the trade secrets,              //
//confidential information and copyrighted works of Mentor Graphics         //
//Corporation and its licensors and are subject to your license agreement   //
//with Mentor Graphics Corporation.                                         //
//                                                                          //
//Use of these deliverables for the purpose of making silicon from an IC    //
//design is limited to the terms and conditions of your license agreement   //
//with Mentor Graphics If you have further questions please contact Mentor  //
//Graphics Customer Support.                                                //
//                                                                          //
//This Mentor Graphics core (m8051w v2002.080) was extracted on             //
//workstation hostid 8316cbec Inventra                                      //
// Serial Interface for M8051W/EW
// 
// $Log: m3s018dy.v,v $
// Revision 1.10  2002/01/24
// fix of the bug of the serial interface
//
// Revision 1.8  2001/11/20
// First checkin of version 2 features and name change
//
// Revision 1.2  2001/10/31
// First parsable verilog for EW
//
// Revision 1.1.1.1  2001/07/17
// Re-imported E-Warp from Farnham filesystem
//
// Revision 1.7  2000/12/13
// Unreachable X-handling states removed
//
// Revision 1.6  2000/10/24
// Multiplier rewritten to improve power consumption.
// Code changes for Leonardo (ECN01372).
// Code changes for formal verification tools (ECN01410).
// MOVX @Ri page address controllable from PORT2I if I/O ports ommitted (ECN01387).
//
// Revision 1.5  2000/02/29
// redundant code removed
//
// Revision 1.4  2000/02/05
// Name change repercussions
//
// Revision 1.3  1999/12/08
// Eliminate user instruction during state transition from interrupt service to
// debug state.
// RTL clean up of redundant declarations and sensitvity list entries.
//
// Revision 1.2  1999/11/03
// Debug mode revisions.
//
// Revision 1.1.1.1  1999/10/28
// "initialization and source check-in for m8051e"
//
// Revision 1.1  1999/10/22
// Initial revision
//
////////////////////////////////////////////////////////////////////////////////

`include "m8051w_defs.v"

module m3s018dy (UART_SFR_DATA, RITI, TXD, RXD, TXDNE, RXDNE,
//*******************************************************************       //
//IMPORTANT NOTICE                                                          //
//================                                                          //
//Copyright Mentor Graphics Corporation 1996 - 1999.  All rights reserved.  //
//This file and associated deliverables are the trade secrets,              //
//confidential information and copyrighted works of Mentor Graphics         //
//Corporation and its licensors and are subject to your license agreement   //
//with Mentor Graphics Corporation.                                         //
//                                                                          //
//Use of these deliverables for the purpose of making silicon from an IC    //
//design is limited to the terms and conditions of your license agreement   //
//with Mentor Graphics If you have further questions please contact Mentor  //
//Graphics Customer Support.                                                //
//                                                                          //
//This Mentor Graphics core (m8051w v2002.080) was extracted on             //
//workstation hostid 8316cbec Inventra                                      //
                 SFR_S_ADDR, DESTIN_ADDR, PORT3I, SMOD, SFRWE, REG_RESULT, RMW,
		 INTERNAL_RESET, T1_OVERFLOW, T2_OVERFLOW, T2_TCLK, T2_RCLK,
                 PCLK_EN, SCLK, CCLK, PCLK);

  output [7:0] UART_SFR_DATA;
  output       RITI;
  output       TXD;
  output       RXD;
  output       TXDNE;
  output       RXDNE;

  input  [7:0] REG_RESULT;
  input  [6:0] SFR_S_ADDR;
  input  [6:0] DESTIN_ADDR;

  input  [0:0] PORT3I;
  input        SMOD;
  input        SFRWE, RMW, INTERNAL_RESET;
  input        T1_OVERFLOW;                          // from timer 1
  input        T2_OVERFLOW;                          // from timer 2 or external
  input        T2_TCLK;                              // from timer 2 or external
  input        T2_RCLK;                              // from timer 2 or external
  input        PCLK_EN;
  input        SCLK, CCLK, PCLK;

  reg    [7:0] SCON, SBUF_RX;                        // Serial Interface SFRs
  reg    [7:0] UART_SFR_DATA;

  reg    [3:0] TX_DIV16, RX_DIV16;                   // Sample clock dividers
  reg          CLK_ROOT_DIV2;                        // SMOD clock divider

  reg   [10:0] TX_SHIFTER;                           // Transmit shift register
  reg          TX_SEND;                              // Transmit state machine
  reg          TX_BEGIN;                             // Transmit buffer reloaded

  reg    [2:0] RX_SAMPLE;                            // start bit edge detector
  reg    [1:0] RX_STATE;                             // Receiver state machine
  reg    [8:0] RX_SHIFTER;                           // Receiver shift register

  reg          OLD_TXD, OLD_RXD;
  reg          MODE0_CLK;                            // External shift clock

  wire         RITI;                                 // OR of serial interrupts
  wire         SCON_WRITE, SBUF_WRITE;               // SFR write strobes

  wire         MODE0;                                // Mode 0 selected
  wire         CLK_ROOT;                             // 8051 baud clock trunk
  wire         TCLK_SAMPLER;                         // Tx baud clock branch
  wire         RCLK_SAMPLER;                         // Rx baud clock branch
  wire         TX_CLK, RX_CLK;                       // Tx & Rx clock qualifiers

  wire         TI_SET;                               // Tx interrupt source
  wire         TXD, RXD;                             // Serial data outputs
  wire         TXDNE, RXDNE;                         // Serial output enables

  wire         MAJORITY;                             // Rx data samples majority
  wire         RX_BEGIN;                             // Start receiving on RXCLK
  wire         RX_BUF_WRITE;                         // Write Rx SFR register

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
// Special Function Register and Interrupt Interfaces to the CPU              //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

  assign SCON_WRITE = SFRWE && DESTIN_ADDR[6:0] == 7'h18;
  assign SBUF_WRITE = SFRWE && DESTIN_ADDR[6:0] == 7'h19;

  // Serial Interface Special Function Registers
  // These registers run on the CPU clock
  always @(posedge CCLK)
  begin: p_cpu_scon
    if (INTERNAL_RESET)
      SCON[7:3] <= 5'b0;
    else if (SCON_WRITE)
      SCON[7:3] <= REG_RESULT[7:3];
  end

  // SCON Hardware-controlled Registers
  // These registers run on the peripheral clock
  // Precedence for writes is 1: SFR access which is not a read-modify-write;
  //                          2: Interrupt source 
  //                          3: Read-modify-write SFR access
  always @(posedge PCLK)
  begin: p_ext_scon
    if (INTERNAL_RESET) begin
      SCON[2:0] <= 3'b0;
    end
    else if (PCLK_EN) begin
      if (RX_BUF_WRITE && !MODE0 && (RMW || !SCON_WRITE))
        SCON[2] <= MAJORITY;
      else if (SCON_WRITE)
        SCON[2] <= REG_RESULT[2];

      if (TI_SET && (RMW || !SCON_WRITE))
        SCON[1] <= 1'b1;
      else if (SCON_WRITE)
        SCON[1] <= REG_RESULT[1];

      if (RX_BUF_WRITE && (RMW || !SCON_WRITE))
        SCON[0] <= 1'b1;
      else if (SCON_WRITE)
        SCON[0] <= REG_RESULT[0];

      if (RX_BUF_WRITE)
        SBUF_RX <= {(MODE0? PORT3I[0]: RX_SHIFTER[8]), RX_SHIFTER[7:1]};
    end
  end

  // Serial Interface SFR multiplexer
// It reads the SFRs
  always @(SCON or SBUF_RX or SFR_S_ADDR)
  begin: p_sfr_mux
    case(SFR_S_ADDR)
      `AddrSCON:    UART_SFR_DATA <= SCON;
      `AddrSBUF:    UART_SFR_DATA <= SBUF_RX;
       default:     UART_SFR_DATA <= 0;
      endcase
  end

  assign RITI   = |SCON[1:0];
  assign MODE0  = ~|SCON[7:6];

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
// Clock Enable Source Multiplexers                                           //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

  assign CLK_ROOT = SCON[6]? T1_OVERFLOW: PCLK_EN;

  always @(posedge PCLK)
  begin: p_SMOD_divider
    if (INTERNAL_RESET)
      CLK_ROOT_DIV2 <= 0;
    else if (CLK_ROOT)
      CLK_ROOT_DIV2 <= !CLK_ROOT_DIV2;
  end

  assign TCLK_SAMPLER = (T2_TCLK && SCON[6] && PCLK_EN)? T2_OVERFLOW:
                          (SMOD || CLK_ROOT_DIV2) && CLK_ROOT;
  assign RCLK_SAMPLER = (T2_RCLK && SCON[6] && PCLK_EN)? T2_OVERFLOW:
                          (SMOD || CLK_ROOT_DIV2) && CLK_ROOT;

  always @(posedge PCLK)
  begin: p_bit_period_dividers
    if (INTERNAL_RESET) begin
      TX_DIV16 <= 4'h0;
      RX_DIV16 <= 4'h0;
      end
    else begin
      if (TCLK_SAMPLER) TX_DIV16 <= TX_DIV16 + 4'h1;
      if (RCLK_SAMPLER)
        if (RX_BEGIN)
          RX_DIV16 <= 4'h2;
        else
          RX_DIV16 <= RX_DIV16 + 4'h1;
    end
  end

  assign TX_CLK = PCLK_EN && (MODE0 || TCLK_SAMPLER && TX_DIV16 == 4'h8);
  assign RX_CLK = PCLK_EN && (MODE0 || RCLK_SAMPLER && RX_DIV16 == 4'h9);

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
// Transmitter                                                                //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

  /* Transmit Shift Register */
  always @(posedge PCLK)
  begin: p_tx_shifter
    if (INTERNAL_RESET) begin
      TX_SHIFTER <= 11'b00000000001;
      TX_BEGIN   <= 0;
    end
    else if (SBUF_WRITE) begin
      TX_SHIFTER[10:1] <= SCON[7]? {1'b1, SCON[3], REG_RESULT}: 
                                   {2'b01, REG_RESULT};
      TX_BEGIN <= 1;
      end
    else if (TX_CLK) begin
      TX_BEGIN <= 0;
      if (TX_SEND)
        TX_SHIFTER <= TX_SHIFTER >> 1;
      else
        if (TX_BEGIN)
          TX_SHIFTER <= MODE0? TX_SHIFTER >> 1: {TX_SHIFTER[10:1], 1'b0};
      end
  end
    
  /* Transmit state machine */
  always @(posedge PCLK)
  begin: p_tx_state
    if (INTERNAL_RESET)
      TX_SEND <= 0;
    else if (TX_CLK)
      TX_SEND <= TX_SEND? |TX_SHIFTER[10:2]: TX_BEGIN;
  end

  assign TI_SET = ~|TX_SHIFTER[10:2] && TX_SEND && TX_CLK;

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
// Receiver                                                                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

  parameter RX_IDLE   = 2'b00;
  parameter RX_START  = 2'b01;
  parameter RX_DATA   = 2'b10;
  parameter RX_BAD    = 2'b11;

  // Edge detector for start bit detection and registers for Rx samples 1 - 3
  always @(posedge PCLK)
  begin: p_sampler
    if (RCLK_SAMPLER) RX_SAMPLE <= {RX_SAMPLE[1:0], PORT3I[0]};
  end

  assign MAJORITY = (RX_SAMPLE[0] & RX_SAMPLE[1]) |( RX_SAMPLE[0] & RX_SAMPLE[2]) |
                    (RX_SAMPLE[1] & RX_SAMPLE[2]);

  /* Receiver state machine */
  //
  // Three states: IDLE, START & receiving DATA.
  // Reception starts in mode 0 once the RI bit is cleared, in other modes a
  // falling edge on RXD starts the state machine.
  // Modes 1 to 3 commence reception with a start bit, followed by eight or nine
  // data bits; mode 0 frame comprise eight data bits only.

  assign RX_BEGIN     = (RX_STATE == RX_IDLE) && SCON[4] &&
                        (MODE0? !SCON[0]: (RX_SAMPLE[1:0] == 2'b10));

  assign RX_BUF_WRITE = RX_CLK && (RX_STATE == RX_DATA) && !RX_SHIFTER[0] &&
                        (MODE0 || (!SCON[0] && (!SCON[5] || MAJORITY)));

  always @(posedge PCLK)
  begin: p_rx_state
    if (INTERNAL_RESET) begin
       RX_STATE     <= RX_IDLE;
       RX_SHIFTER   <= 9'h1FF;
    end
    else if (RCLK_SAMPLER || (MODE0 && PCLK_EN)) begin
      if (RX_STATE == RX_IDLE) begin
        RX_SHIFTER <= {1'b1, !MODE0, 7'h7F};
        if (RX_BEGIN)
          RX_STATE <= MODE0? RX_DATA: RX_START;
      end
      else if (RX_CLK) begin
        if (!SCON[4])
          RX_STATE <= RX_IDLE;
        else begin
          case(RX_STATE)
            RX_START:  RX_STATE <= MAJORITY?      RX_IDLE: RX_DATA;
            RX_DATA:   RX_STATE <= RX_SHIFTER[0]? RX_DATA: RX_IDLE;
            default:   RX_STATE <= RX_IDLE;
          endcase
          if ((RX_STATE == RX_START) || (RX_STATE == RX_DATA))
            RX_SHIFTER <= {MAJORITY, (MODE0? PORT3I[0]: RX_SHIFTER[8]),
                          RX_SHIFTER[7:1]};
        end
      end
    end
  end

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
// Mode 0 external shift clock                                                //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

  always @(posedge SCLK)
  begin: p_m0_clk
    MODE0_CLK <= (!TX_SEND &&  (RX_STATE == RX_IDLE)) || PCLK_EN;
  end

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
// Port pin drivers and mappings                                              //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

  always @(posedge PCLK)
  begin: p_port_ens
    if (INTERNAL_RESET) begin
      OLD_TXD <= 1;
      OLD_RXD <= 1;
    end
    else if (PCLK_EN)
    begin
      OLD_TXD <= TXD;
      OLD_RXD <= RXD;
    end
  end
  
  assign TXD = MODE0? MODE0_CLK: TX_SHIFTER[0];
  assign RXD = !MODE0 || TX_SHIFTER[0];

  // Drive Enables for serial functions
  assign TXDNE = TXD && OLD_TXD;
  assign RXDNE = RXD && OLD_RXD;

endmodule
